#pragma once
#include <string>
#include <vector>
#include<fstream>
#include<filesystem>
#include<unordered_map>
#include<sys/stat.h>
//响应状态码的描述信息

std::unordered_map<int, std::string> HTTP_STATUS_CODES = {
    // 1xx Informational
    {100, "Continue"},
    {101, "Switching Protocols"},
    {102, "Processing"},
    {103, "Early Hints"},

    // 2xx Success
    {200, "OK"},
    {201, "Created"},
    {202, "Accepted"},
    {203, "Non-Authoritative Information"},
    {204, "No Content"},
    {205, "Reset Content"},
    {206, "Partial Content"},
    {207, "Multi-Status"},
    {208, "Already Reported"},
    {226, "IM Used"},

    // 3xx Redirection
    {300, "Multiple Choices"},
    {301, "Moved Permanently"},
    {302, "Found"},
    {303, "See Other"},
    {304, "Not Modified"},
    {305, "Use Proxy"},
    {307, "Temporary Redirect"},
    {308, "Permanent Redirect"},

    // 4xx Client Errors
    {400, "Bad Request"},
    {401, "Unauthorized"},
    {402, "Payment Required"},
    {403, "Forbidden"},
    {404, "Not Found"},
    {405, "Method Not Allowed"},
    {406, "Not Acceptable"},
    {407, "Proxy Authentication Required"},
    {408, "Request Timeout"},
    {409, "Conflict"},
    {410, "Gone"},
    {411, "Length Required"},
    {412, "Precondition Failed"},
    {413, "Payload Too Large"},
    {414, "URI Too Long"},
    {415, "Unsupported Media Type"},
    {416, "Range Not Satisfiable"},
    {417, "Expectation Failed"},
    {418, "I'm a teapot"},
    {421, "Misdirected Request"},
    {422, "Unprocessable Entity"},
    {423, "Locked"},
    {424, "Failed Dependency"},
    {425, "Too Early"},
    {426, "Upgrade Required"},
    {428, "Precondition Required"},
    {429, "Too Many Requests"},
    {431, "Request Header Fields Too Large"},
    {451, "Unavailable For Legal Reasons"},

    // 5xx Server Errors
    {500, "Internal Server Error"},
    {501, "Not Implemented"},
    {502, "Bad Gateway"},
    {503, "Service Unavailable"},
    {504, "Gateway Timeout"},
    {505, "HTTP Version Not Supported"},
    {506, "Variant Also Negotiates"},
    {507, "Insufficient Storage"},
    {508, "Loop Detected"},
    {510, "Not Extended"},
    {511, "Network Authentication Required"},

    // Nginx扩展
    {444, "No Response"},
    {494, "Request Header Too Large"},
    {495, "SSL Certificate Error"},
    {496, "SSL Certificate Required"},
    {497, "HTTP Request Sent to HTTPS Port"},
    {499, "Client Closed Request"}
};
std::unordered_map<std::string, std::string> MIME_TYPES = {
    // 文本类型
    { ".txt", "text/plain" },
    { ".html", "text/html" },
    { ".htm", "text/html" },
    { ".css", "text/css" },
    { ".js", "text/javascript" },
    { ".json", "application/json" },
    { ".csv", "text/csv" },
    { ".xml", "text/xml" },
    { ".md", "text/markdown" },
    
    // 图像类型
    { ".jpg", "image/jpeg" },
    { ".jpeg", "image/jpeg" },
    { ".png", "image/png" },
    { ".gif", "image/gif" },
    { ".svg", "image/svg+xml" },
    { ".webp", "image/webp" },
    { ".bmp", "image/bmp" },
    { ".ico", "image/x-icon" },
    
    // 音频类型
    { ".mp3", "audio/mpeg" },
    { ".ogg", "audio/ogg" },
    { ".wav", "audio/wav" },
    { ".webm", "audio/webm" },
    { ".aac", "audio/aac" },
    
    // 视频类型
    { ".mp4", "video/mp4" },
    { ".ogv", "video/ogg" },
    { ".webm", "video/webm" },
    { ".avi", "video/x-msvideo" },
    { ".mov", "video/quicktime" },
    
    // 应用程序类型
    { ".pdf", "application/pdf" },
    { ".zip", "application/zip" },
    { ".rar", "application/x-rar-compressed" },
    { ".tar", "application/x-tar" },
    { ".7z", "application/x-7z-compressed" },
    { ".gz", "application/gzip" },
    { ".doc", "application/msword" },
    { ".xls", "application/vnd.ms-excel" },
    { ".ppt", "application/vnd.ms-powerpoint" },
    { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
    { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
    { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
    
    // 字体类型
    { ".ttf", "font/ttf" },
    { ".otf", "font/otf" },
    { ".woff", "font/woff" },
    { ".woff2", "font/woff2" },
    
    // 默认类型
    { ".bin", "application/octet-stream" },
    { "", "application/octet-stream" }
};


class Util{//协议支持时所需要的零碎功能
    public:
        
        //字符串分割函数,将src字符串按照sep字符为分割符标志进⾏分割，得到的各个字串放到arry中，最终返回字串的数量
        static size_t Split(const std::string &src,const std::string &sep,std::vector<std::string> *array)//分割字符串
        {
            int index = 0;//当前正在处理的位置(从0开始寻找)
            while(index<src.size()){
                int pos = src.find(sep,index);//从index处开始查找sep字符            
                if(pos==std::string::npos){
                    array->push_back(src.substr(index));
                    return array->size();
                }else{
                    if(pos == index){
                        index = pos+sep.size();
                        continue;
                    }
                    array->push_back(src.substr(index,pos-index));
                    index = pos+sep.size();
                }
            }
            return array->size();   
        }

        static bool ReadFile(const std::string &filename,std::string *buf)//读取文件,将读到的内容存放到buf中
        {
            std::ifstream fin(filename.c_str(),std::ios::in|std::ios::binary);
            if(!fin.is_open()){//判断文件是否打开成功
                printf("open file %s failed\n",filename.c_str());
                return false;
            }
            fin.seekg(0,std::ios::end);//移动文件指针到文件末尾
            size_t fsize = fin.tellg();//通过获取文件指针当前位置来判断文件大小
            fin.seekg(0,std::ios::beg);//移动文件指针到文件开头
            buf->resize(fsize);//分配buf的空间
            fin.read(&(*buf)[0],fsize);//读取文件内容到buf中
            if(!fin.good()){//判断是否读取成功
                printf("read file %s failed\n",filename.c_str());
                return false;
            }
            fin.close();
            return true;
        }
        static bool WriteFile(const std::string &filename,const std::string &data)//向文件写入数据
        {
            std::ofstream fout(filename.c_str(),std::ios::out|std::ios::binary|std::ios::trunc);
            if(!fout.is_open()){
                printf("open file %s failed\n",filename.c_str());
                return false;
            }
            fout.write(data.c_str(),data.size());
            if(!fout.good()){
                printf("write file %s failed\n",filename.c_str());
                return false;
            }
            fout.close();
            return true;
        }


        //URL编码，避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产⽣歧义
        //编码格式：将特殊字符的ascii值，转换为两个16进制字符，前缀%   C++ -> C%2B%2B
        // 不编码的特殊字符： RFC3986⽂档规定 . - _ ~ 字⺟，数字属于绝对不编码字符
        //RFC3986⽂档规定，编码格式 %HH
        //W3C标准中规定，查询字符串中的空格，需要编码为+， 解码则是+转空格
        static std::string EncodeUrl(const std::string url,bool sapce_to_plus)
        {
            std::string result;
            for( auto &c : url ){
                if(c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c)){// RFC3986⽂档规定 . - _ ~ 字⺟，数字属于绝对不编码字符
                    result+=c;
                    continue;
                }else if(c == ' ' && sapce_to_plus){
                    result+='+';
                    continue;
                }else{
                    //剩下的字符都是需要编码成为 %HH 格式
                    char temp[4];
                    sprintf(temp,"%%%02X",c);
                    result+=temp;
                }
            }
            return result;
        }
        static char HexToDec(char c)//将单个16进制字符（0-9，a-f，A-F）转换为数字字符（0-15）
        {
            if(c >= '0' && c <= '9'){
                return c - '0';
            }else if(c >= 'a' && c <= 'z'){
                return c - 'a' + 10;
            }else if(c >= 'A' && c <= 'Z'){
                return c - 'A' + 10;
            }
            return -1;
        }
        static std::string DecodeUrl(const std::string url,bool sapce_to_plus)
        {
            //遇到了%，则将紧随其后的2个字符，转换为数字，第⼀个数字左移4位，然后加上第二个数字  + -> 2b  %2b->2 << 4 + 11
            std::string result;
            for(int i = 0;i < url.size();i++){
                if(url[i] == '+' && sapce_to_plus){
                    result+=' ';
                    continue;
                }else if(url[i] == '%' && i+2 < url.size()){
                    char c1 = HexToDec(url[i+1]);
                    char c2 = HexToDec(url[i+2]);
                    char c = c1*16 + c2;//ASCII码所对应的字符
                    result+=c;
                    i+=2;
                    continue;
                }else{
                    result+=url[i];
                }
            }
            return result;
        }

        static std::string StatusDescription(int status)//响应状态码的描述信息获取
        {
            return HTTP_STATUS_CODES.find(status) == HTTP_STATUS_CODES.end() ? "Unknown" : HTTP_STATUS_CODES[status];
        }
        static std::string ExtMime(const std::string &filename)//根据⽂件后缀名获取⽂件mime
        {   
            auto pos = filename.rfind('.');
            if(pos == std::string::npos){
                return "application/octet-stream";
            }
            std::string ext = filename.substr(pos);//获取文件后缀名
            auto it = MIME_TYPES.find(ext);
            if(it == MIME_TYPES.end()){
                return "application/octet-stream";
            }
            return it->second;
        }

        static bool IsDirectory(const std::string &filename)//判断⼀个⽂件是否是⼀个⽬录
        {
            return std::filesystem::is_directory(filename);//c++17标准库中的文件系统库，可以直接判断
            // struct stat st;
            // int ret = stat(filename.c_str(),&st);//调用 POSIX 标准库函数 stat()，获取文件信息
            // //ret < 0：表示调用失败（如文件不存在、无权限等），此时返回 false。
            // //ret == 0：表示成功获取文件信息，继续检查文件类型。
            // if(ret < 0){
            //     return false;
            // }
            // return S_ISDIR(st.st_mode);//st.st_mode 存储了文件的类型和权限信息。
            // S_ISDIR 是一个宏，用于检查 st_mode 是否表示目录：
            // 如果是目录（如  /usr/local/bin ），返回 true。
            // 如果是普通文件、管道、套接字等其他类型，返回 false。
        }
        static bool IsRegularFile(std::string &filename)//判断⼀个⽂件是否是⼀个常规⽂件
        {
            return std::filesystem::is_regular_file(filename);//c++17标准库中的文件系统库，可以直接判断文件类型
            // struct stat st;
            // int ret = stat(filename.c_str(),&st);//调用 POSIX 标准库函数 stat()，获取文件信息
            // //ret < 0：表示调用失败（如文件不存在、无权限等），此时返回 false。
            // //ret == 0：表示成功获取文件信息，继续检查文件类型。
            // if(ret < 0){
            //     return false;
            // }
            // return S_ISREG(st.st_mode);//st.st_mode 存储了文件的类型和权限信息。
            // S_ISREG 是一个宏，用于检查 st_mode 是否表示普通文件：
            // 如果是普通文件（如 .txt、.exe 等），返回 true。
            // 如果是目录、管道、套接字等其他类型，返回 false。
        }

        //http请求的资源路径有效性判断
        // /index.html  --- 前边的/叫做相对根⽬录 映射的是某个服务器上的⼦⽬录
        // 想表达的意思就是，客⼾端只能请求相对根⽬录中的资源，其他地⽅的资源都不予理会
        // 如/../login, 这个路径中的..会让路径的查找跑到相对根⽬录之外，这是不合理的，不安全的
        static bool ValidPath(const std::string &path)//判断⽤户提供的路径是否合法
        {
            //思想：按照/进⾏路径分割，根据有多少⼦⽬录，计算⽬录深度，有多少层，深度不能⼩于0
            std::vector<std::string> subdir;
            Split(path,"/",&subdir);
            int depth = 0;
            for(auto &dir : subdir){
                if(dir == ".."){
                    depth--;
                    if(depth < 0)
                        return false;
                    continue;
                }else{
                    depth++;
                }
            }
            return true;
        }
};

